#!/usr/bin/env python
""" Contains the applications entry point. Deals with the main GUI and loads all other components. """
import pygtk
pygtk.require('2.0')
import gtk
gtk.gdk.threads_init()
import os
import logging
import pickle
from ngsocks import Socks4Server, FILTER, hex
import threading

VERSION = "0.1"
NAME = "ngproxi"

DEFAULT_CONFIG = {
               "socks_port":1080,
               "socks_bind_address": "localhost",
               "socks_server_timeout":30,
               "socks_client_timeout":600,
               "socks_buffer_size":1024,
               "msg_line_ending":"\r\n",
               "log_level":10,
               }

class GtkLogHandler(logging.Handler):
    def __init__(self, buffer):
        logging.Handler.__init__(self)
        self.textbuffer = buffer
        
    def emit(self, record):
        message = self.formatter.format(record)
        message += "\n"
        self.textbuffer.insert(self.textbuffer.get_end_iter(), message)

class NGInterceptUI:
    """the UI class is the base class all GUI"""
    def __init__(self):
        self.classname = self.__class__.__name__
        self.uifile = "ngproxi.glade"
        self.wtree = gtk.Builder()
        self.wtree.add_from_file(self.uifile)
        
        for widget in self.wtree.get_objects():
            self.__dict__[widget.get_name()] = widget
        
        self.MainUI.show_all()     
        self.wtree.connect_signals(self)
        self.MainUI.connect("delete_event", self.on_menu_quit_activate)
        
        self.MainUI.set_title(NAME + " " + VERSION)
        self.init_logging()
        self.init_config()
              
        
        self.raw_buffer = self.textview_raw.get_buffer()
        self.hex_buffer = self.textview_hex.get_buffer()
        
        self.proxy = Socks4Server(self.config)
        self.init_events()
        self.message = None
        
        self.logger.info("ngintercept started.")        


        
    def init_logging(self):
        self.logbuffer = gtk.TextBuffer()
        self.textviewLog.set_buffer(self.logbuffer)
        
        self.logger = logging.getLogger("ngseca")
        log_handler = GtkLogHandler(self.logbuffer)
        self.logger.setLevel(DEFAULT_CONFIG["log_level"])
        log_formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
        log_handler.setFormatter(log_formatter)
        self.logger.addHandler(log_handler)
        self.logger.info("logging initialized.")
        
            
    def init_config(self):
        pwd = os.getcwd()
        config_file = pwd + os.sep + "config.cp"
        self.config = DEFAULT_CONFIG
        if os.path.isfile(config_file):
            try:
                f = open(config_file, "r")
                self.config = pickle.load(f)
            except Exception, e:
                self.statusmsg("file error, check log for details.")
                self.logger.exception(e)
            finally:
                f.close()
        self.configoptions = gtk.ListStore(str, str) 
        self.treeview_config.set_model(self.configoptions)
        
        self.option_column = gtk.TreeViewColumn("Option", gtk.CellRendererText(), text=0)
        self.treeview_config.append_column(self.option_column)    
                  
        renderer = gtk.CellRendererText()
        renderer.set_property('editable', True)
        renderer.connect('edited', self.cell_edited_callback, (self.configoptions, 1))

        self.treeview_config.insert_column_with_attributes(-1, "Value", renderer, text=1)
    
        self.configoptions.set_sort_column_id(0, gtk.SORT_ASCENDING)
        
        for option,val in self.config.items():
            self.configoptions.append((option,val))   
        self.logger.info("config initialized.")     
        
        
    def init_events(self):
        self.proxy.attach("client_connected", self.client_connected)
        self.proxy.attach("exception",self.handle_exception)
        self.proxy.attach("msg_received", self.msg_received)
              
        
    def save_config(self, widget, data=None):
        pwd = os.getcwd()
        config_file = pwd + os.sep + "config.cp"
        try:
            f = open(config_file, "wb")
            pickle.dump(self.config, f)
            self.logger.info("config saved.") 
        except Exception, e:
            self.statusmsg("file error, check log for details.")
            self.logger.exception(e)
        finally:
            f.close()
            
    def cell_edited_callback(self, cell, path, new_text, user_data):
        liststore, column = user_data
        liststore[path][column] = new_text
        self.config[liststore[path][column-1]] = new_text
        
    def start_proxy(self, widget):
        self.proxy.start()
        self.statusmsg("proxy running.")  
        self.label_proxy_status.set_text("1")
        
    def stop_proxy(self, widget):
        self.proxy.stop()
        self.label_proxy_status.set_text("0")
        self.statusmsg("proxy stopped.") 
        
    def client_connected(self, eventdict):
        self.label_client_status.set_text("1")
        self.statusmsg("client connected.")      
        
    def on_combobox_client_changed(self, widget):
        active = self.combobox_client.get_active()
        if active < 0:
            return None    
            
    def handle_exception(self, eventdict):
        self.statusmsg("error. see log for details.")
        self.logger.exception(eventdict["exception"])
        self.label_client_status.set_text("0")
        
    def msg_received(self, eventdict):
        if not self.proxy.edit:
            self.display_msg(self.proxy.messages.get())
            self.proxy.edit = True
                
    def display_msg(self, message):
        self.message = message
        self.raw_buffer.set_text(message[1].translate(FILTER))
        self.hex_buffer.set_text(hex(message[1]))
     
    def reset_msg(self):
        start,end = self.raw_buffer.get_bounds()
        self.raw_buffer.delete(start, end)  
        start,end = self.hex_buffer.get_bounds()
        self.hex_buffer.delete(start, end)   
        
    def send_msg(self, widget):
        if self.proxy.edit == False:
            return
        start,end = self.raw_buffer.get_bounds()
        self.message[1] = self.raw_buffer.get_text(start, end) + self.config["msg_line_ending"]
        self.proxy.send(self.message[0], self.message[1])
        self.reset_msg()
        self.proxy.edit = False
        
    def drop_msg(self, widget):
        self.proxy.drop(self.message)
        self.proxy.edit = False   
        
    def toggle_in(self, widget, data=None):
        self.proxy.intercept_in = not self.proxy.intercept_in
        
    def toggle_out(self, widget, data=None):
        self.proxy.intercept_out = not self.proxy.intercept_out
            
    def on_menu_quit_activate(self, widget, data=None):
        self.MainUI.destroy()
        gtk.main_quit()
        
    def statusmsg(self, text):
        """ displays a message in the status bar"""
        self.statusbar.pop(-1)
        self.statusbar.push(-1, text)
        self.logger.info(text)
        
if __name__ == "__main__":
    app=NGInterceptUI()
    gtk.main()     
